﻿using Logs.Contents;
using Microsoft.Extensions.Logging;
using Logs.Formats;
using Logs.LogCore.Abstractions;
using Utility.Exceptions;
using Utility.Extensions;
using Logs.LogCore.Extensions;
using Utility.Helpers;
using Microsoft.Extensions.DependencyInjection;
using Enum = System.Enum;

namespace Logs.Log4net
{
    /// <summary>
    /// Log4日志提供程序
    /// </summary>
    public class Log4Provider : ILogProvider, ILoggerProvider, ILogger
    {
        /// <summary>
        /// log4net日志操作
        /// </summary>
        private readonly log4net.ILog _logger;
        /// <summary>
        /// 日志格式化器
        /// </summary>
        private readonly ILogFormat _format;

        /// <summary>
        /// 日志仓库名称
        /// </summary>
        public const string LOG_REPOSITORY_NAME = "Logs";

        private readonly string _loggerName;

        /// <summary>
        /// 初始化日志
        /// </summary>
        /// <param name="logName">日志名称</param>
        /// <param name="format">日志格式化器</param>
        public Log4Provider(string logName, ILogFormat format = null)
        {
            _loggerName = logName;
            _logger = GetLogger(logName);
            _format = format;
        }

        /// <summary>
        /// 获取Log4net日志操作
        /// </summary>
        /// <param name="logName">日志名称</param>
        public static log4net.ILog GetLogger(string logName)
        {
            return log4net.LogManager.GetLogger(LOG_REPOSITORY_NAME, logName);
        }

        /// <summary>
        /// 日志名称
        /// </summary>
        public string LogName => _logger.Logger.Name;

        /// <summary>
        /// 调试级别是否启用
        /// </summary>
        public bool IsDebugEnabled => _logger.IsDebugEnabled;

        /// <summary>
        /// 跟踪级别是否启用
        /// </summary>
        public bool IsTraceEnabled => false;

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="level">日志等级</param>
        /// <param name="content">日志内容</param>
        public void WriteLog(LogLevel level, ILogContent content)
        {
            var provider = GetFormatProvider();
            switch (level)
            {
                case LogLevel.Debug:
                    _logger.DebugFormat(provider, "{0}", content);
                    return;
                case LogLevel.Information:
                    _logger.InfoFormat(provider, "{0}", content);
                    return;
                case LogLevel.Warning:
                    _logger.WarnFormat(provider, "{0}", content);
                    return;
                case LogLevel.Error:
                    _logger.ErrorFormat(provider, "{0}", content);
                    return;
                case LogLevel.Critical:
                    _logger.FatalFormat(provider, "{0}", content);
                    return;
                default:
                    return;
            }
        }

        /// <summary>
        /// 获取格式化提供程序
        /// </summary>
        private IFormatProvider GetFormatProvider()
        {
            if (_format == null)
                return null;
            return new FormatProvider(_format);
        }

        #region ILoggerProvider 实现

        public ILogger CreateLogger(string categoryName)
        {
            return new Log4Provider(categoryName, _format);
        }

        public void Dispose()
        {
        }

        #endregion

        #region ILogger 实现

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            var content = GetContent(logLevel, formatter(state, exception), exception);
            WriteLog(logLevel, content);
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            switch (logLevel)
            {
                case LogLevel.Critical:
                    return _logger.IsFatalEnabled;
                case LogLevel.Debug:
                case LogLevel.Trace:
                    return _logger.IsDebugEnabled;
                case LogLevel.Error:
                    return _logger.IsErrorEnabled;
                case LogLevel.Information:
                    return _logger.IsInfoEnabled;
                case LogLevel.Warning:
                    return _logger.IsWarnEnabled;
                case LogLevel.None:
                    return false;

                default:
                    throw new ArgumentOutOfRangeException(nameof(logLevel));
            }
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }

        /// <summary>
        /// 获取日志内容
        /// </summary>
        /// <param name="logLevel"></param>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        /// <returns></returns>
        private ILogContent GetContent(LogLevel logLevel, string message, Exception exception)
        {
            var content = new LogContent();
            var context = ServiceLocator.Instance.GetService<ILogContext>();
            content.LogName = _loggerName;
            content.Level = Enum.GetName(logLevel);
            content.Exception = exception == null ? null : new Warning(exception);
            content.TraceId = context.TraceId;
            content.OperationTime = DateTime.Now.ToMillisecondString();
            content.Duration = context.Stopwatch.Elapsed.Description();
            content.Ip = context.Ip;
            content.Host = context.Host;
            content.ThreadId = Thread.CurrentThread.ManagedThreadId.ToString();
            content.Browser = context.Browser;
            content.Url = context.Url;
            //content.UserId = Ioc.Create<ISession>().UserId;
            content.Content(message);
            return content;
        }
        #endregion
    }
}
